Strict
Import mojo
Import agfx


#Rem
Header: VERLET Physic - point
#End


#Rem
Summary: TVerletPoint class
#End
Class TVerletPoint

	#Rem
	Summary: current point position
	#End	
	Field pos:TVector2
	
	#Rem
	Summary: previous point position
	#End
	Field old_pos:TVector2		
	
	#Rem
	Summary: impulse force
	#End
	Field force:TVector2 		
	
	#Rem
	Summary: gravity
	#End
	Field gravity:TVector2 		
	
	#Rem
	Summary: friction (not yet implemented)
	#End
	Field friction:Float
	
	#Rem
	Summary: mass of point 
	#End
	Field mass:Float
	
	#Rem
	Summary: active 
	when is true is dynamic, when false works like static
	#End
	Field active:Int
	
	#Rem
	Summary: reference to verlet group
	#End
	Field parent:TVerletGroup
	
	#Rem
	Summary: point radius
	default = 0
	#End
	Field radius:Float = 4.0
	

	#Rem
	Summary: constructor
	#End
	Method New( x:Float,y:Float, mass:Float, active:Int = True)
		
		Self.pos = New TVector2(x,y)		
		Self.old_pos = New TVector2(x,y)	
		Self.force = New TVector2(0,0)
		Self.gravity = New TVector2(0,0)
		Self.mass = mass
		Self.active = active	
	End
	
	#Rem
	Summary: static function to create point
	#End
	Function Create:TVerletPoint( x:Float,y:Float, mass:Float, active:Int = True)
		Return  New TVerletPoint(x,y,mass,active)		
	End
	
	#Rem
	Summary: set force to point
	#End
	Method SetForce:Void(f:TVector2)
		Self.force.Set(f)
	End Method
	
	#Rem
	Summary: set gravity
	#End
	Method SetGravity:Void(f:TVector2)
		Self.gravity.Set(f)
	End Method
	
	#Rem
	Summary: Accelerate point - zrychlenie
	#End
	Method Accelerate:Void()
		
		Self.pos.x += (Self.force.x+Self.gravity.x) * AppSpeed() * S_TIMESTEP * S_TIMESTEP
		Self.pos.y += (Self.force.y+Self.gravity.y) * AppSpeed() * S_TIMESTEP * S_TIMESTEP
				
	End Method
	
	#Rem
	Summary: Inertia - zorvacnost
	#End
	Method Inertia:Void()
		Local x:Float  = Self.pos.x*2.0 - Self.old_pos.x;
    	Local y:Float  = Self.pos.y*2.0 - Self.old_pos.y;
	    Self.old_pos.Set(Self.pos)	    
	    Self.pos.Set(x,y)
	End Method
	
	#Rem
	Summary: Update
	#End
	Method Update:Void()
	
		If Not Self.active Then Return
		
			Self.Accelerate()
			Self.Inertia()
		#Rem
		Local tmp1:TVector2 = New TVector2(Self.pos)
		
		Local tmp2:TVector2 = VecMult(Self.force, AppSpeed() * S_TIMESTEP * S_TIMESTEP)
		
		Self.old_pos.Sub(tmp2)
		
		Self.pos.Add(Self.old_pos)
		
		Self.pos.Sub(tmp1)
		
		Self.old_pos.Set(tmp1)
		
		#End
		
		'[1]
		'old_pos.x = (2 * pos.x + old_pos.x) / 3
		'old_pos.y = (2 * pos.y + old_pos.y) / 3
		
		'[2]
		'????? FRICTION EXPERIMENT
		'Local fr:TVector2 = VecSub(Self.pos,Self.old_pos)		
		'fr.Mult(0.1)
		'Self.old_pos.Add(fr)
		
		Self.force.Set(0,0)
	
	End 
	
	#Rem
	Summary: render debug information only
	#End
	Method Render:Void()
		If Self.active Then
			SetColor 0,0,255		
			DrawOval(Self.pos.x-Self.radius/2.0,Self.pos.y-Self.radius/2.0, Self.radius,Self.radius)
		Else
			SetColor 255,255,255
			DrawRect(Self.pos.x-4,Self.pos.y-4, 8,8)
		EndIf
		
	End Method
	
	#Rem
	Summary: translate point
	x:Float				- x or delta x
	y:Float				- y or delat y
	reset:Int = False	- = false : works like impulse 
						- = true :  translate object to position
	#End
	Method Translate:Void( x:Float,y:Float, reset:Int = False)
	
		Self.pos.Add(x,y)
			
		' reset movement
		If reset Then
			Self.old_pos.Set(Self.pos)			
		EndIf
		
	End Method
	
	#Rem
	Summary: Rotate
	dir:Float		angle or angular impulse
	reset:Int = False	if False then works like impulse, when True then is group rotated
	#End
	Method Rotate:Void( dir:Float, center:TVector2, reset:Int = False)
		
		Local r:TVector2 = VecSub(Self.pos,center)
				
		Self.pos.Set(r.x * Cos(dir) - r.y * Sin(dir) , r.x * Sin(dir) + r.y * Cos(dir))
		
		Self.pos.Add(center)
		
		' reset movement
		If reset Then
			Self.old_pos.Set(Self.pos)		
		EndIf
	End Method
	
	#Rem
	Summary: Check and iterate range collision 
	#End
	Method IterateRangeCollision:Void(range:TBBox)
		
		' top
		If Self.pos.y < range.Min.y Then
			Self.pos.y = range.Min.y
		End If
		
		' bottom
		If Self.pos.y > range.Max.y Then 
			Self.pos.y = range.Max.y
			' full friction
			Self.old_pos.x = Self.pos.x
		EndIf
		
		' left, right
		If Self.pos.x < range.Min.x Then
			Self.pos.x = range.Min.x
		ElseIf Self.pos.x > range.Max.x Then
			Self.pos.x = range.Max.x
		EndIf
	
	End
	
	Method ToString:String()
		Return Self.pos.ToString()
	End Method
End